import base64

import pickle
from django.shortcuts import render
from rest_framework import status
from rest_framework.views import APIView
from rest_framework.response import Response

from goods.models import SKU
from .serializers import CartSerializer
from django_redis import get_redis_connection
from .serializers import CartSKUSerializer,CartDeleteSerializer
# Create your views here.

"""
增加数据
1.先提交数据，后端要接收到数据
2.接收数据之后对数据进行校验
3.获取到校验的数据之后，根据用户的登陆状态，进行处理
4.登陆用户保存在Redis中
5.非登陆用户保存在cookies中

"""

class CartView(APIView):

    """
    购物车

    POST        /cart/ 添加商品到购物车
    GET         /cart/ 获取购物车数据
    PUT         /cart/ 修改购物车数据
    DELETE      /cart/删除购物车数据

    """
    #如果用户没有传token 或者传递的是正确的token 都可以执行post的方法
    #如果用户伪造token　不能添加到购物车　我们需要将对用户的验证先去掉

    #我们重写　对用户的认证方法　就可以让伪造的token进入到　添加购物车的功能
    def perform_authentication(self,request):
        pass

    # 添加购物车模块
    def post(self,request):
        """
        增加数据
        # 1. 先提交数据, 后端要接受到数据
        # 2. 接收数据之后对数据进行校验
        # 3. 获取到校验的数据之后,根据用户的登录状态 进行处理
        # 4. 登录用户保存在Redis
        # 5. 非登录用户保存在cookie中

        """

        # 因为我们重写了 perform_authentication 所以我们获取用户信息的时候
        # 对于伪造的用户信息 获取可能会存在异常,所以要捕获一下

        # 1. 先提交数据, 后端要接受到数据 (sku_id,count,selected)
        # 2. 接收数据之后对数据进行校验
        serializer = CartSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        # 3. 获取到校验的数据之后,根据用户的登录状态 进行处理
        sku_id = serializer.data.get('sku_id')
        count = serializer.data.get('count')
        selected = serializer.data.get('selected')

        #对于伪造的用户信息 获取可能会存在异常,所以要捕获一下
        try:
            user = request.user
        except Exception:
            user = None

        # 判断用户是否为登录用户
        # request.user.is_authenticated
        if user is not None and user.is_authenticated:
            # 4. 登录用户保存在Redis
            # 4.1 连接redis
            redis_conn = get_redis_connection('cart')
            # 4.2 添加数据
            # sku_id:count hash
            # sku_id    set 是否选中

            #添加redis的时候 有可能之前已经存在了该商品
            # redis_conn.hset('cart_%s'%user.id,sku_id,count)

            # hincrby 如果 redis很中没有值,则添加此数据
            # hincrby 如果 redis很有值,则累加此数据
            pl = redis_conn.pipeline()

            pl.hincrby('cart_%s'%user.id,sku_id,count)


            if selected:
                pl.sadd('cart_selected_%s'%user.id,sku_id)
            # 4.3 返回响应
            pl.execute()
            return Response(serializer.data)

        else:
            # 5. 非登录用户保存在cookie中

            # 5.1 查看cookie中是否存在购物车数据
            cookie_cart = request.COOKIES.get('cart')
            if cookie_cart is not None:
                #购物车有数据
                decode = base64.b64decode(cookie_cart)
                cart = pickle.loads(decode)
                # cart = pickle.loads(base64.b64decode(cookie_cart.encode()))

            else:
                #购物车没有数据
                cart = {}

            #5.2 更新数据 ,判断商品是否存在,存在则累加个数,不存在则直接添加
            # sku_id: {count:xxx,selected:xxx}
            # {1:{'count':5,'selected':True},2:{'count':5,'selected':True},...}

            if sku_id in cart:
                #存在则累加个数
                #获取原来个数
                orginal_count = cart[sku_id]['count']
                #累加
                count += orginal_count
            # 如果有则更新,如果没有则添加
            cart[sku_id]={
                'count':count,
                'selected':selected
            }

            #5.3 返回响应
            response = Response(serializer.data)

            dumps = pickle.dumps(cart)
            # b64encode 返回的是 bytes类型,所以进行 decode() 操作
            cookie_str = base64.b64encode(dumps).decode()
            #
            #设置cookie数据
            response.set_cookie('cart',cookie_str,24*3600)

            return response

    def get(self, request):

        """
        # 1. 根据用户的信息进行判断
        # 2. 登录用户获取redis数据
        # 3. 未登录用户获取cookie数据
        # 4. 查询商品数据,进行序列化操作
        #      4.1 获取id信息 [数据结构必须 redis和cookie一致]
        #     4.2 根据id信息查询商品信息(模型)
        #     4.3 需要将模型转换为JSON
        """

        # 1. 根据用户的信息进行判断
        try:
            user = request.user
        except Exception:
            user = None
        # request.user.is_authenticated
        # [数据结构必须 redis和cookie一致]
        # 无外乎就是将cookie数据结构转换为redis的结构
        # 无外乎就是将redis数据结构转换为cookie的结构
        # 因为cookie数据是一个字典,所以我们把redis数据转换为 cookie的结构

        if user is not None and user.is_authenticated:
            # 2. 登录用户获取redis数据

            # 2.1 连接redis
            redis_conn = get_redis_connection('cart')
            # 2.2 获取所有数据, 从redis获取的数据是 bytes类型
            redis_cart = redis_conn.hgetall('cart_%s' % user.id)
            # hash {sku_id:count,sku_id:count}

            redis_selected_ids = redis_conn.smembers('cart_selected_%s' % user.id)
            # [sku_id,sku_id]

            # 2.3 拼接成 cookie的数据结构
            # cart = {sku_id:{'count':xxx,'selected':xxxx},...}
            cart = {}
            # 对 redis_cart 进行遍历, 因为 redis-cart 里边存放了所有数据
            for sku_id, count in redis_cart.items():

                if sku_id in redis_selected_ids:
                    selected = True
                else:
                    selected = False

                cart[sku_id.decode()] = {
                    'count': int(count),
                    # 'selected':sku_id in redis_selected_ids
                    'selected': selected
                }

        else:
            # 3. 未登录用户获取cookie数据
            cookie_cart = request.COOKIES.get('cart')

            # 对cookie数据进行判断
            if cookie_cart is not None:
                # 需要转换数据


                cart = pickle.loads(base64.b64decode(cookie_cart))

            else:
                cart = {}

        # 4. 查询商品数据,进行序列化操作
        # cart
        # cart = {sku_id:{'count':xxx,'selected':xxxx},...}
        #      4.1 获取id信息
        ids = cart.keys()
        # [1,2,3,4]
        #     4.2 根据id信息查询商品信息(模型)
        skus = SKU.objects.filter(pk__in=ids)
        # skus 中的 SKU 没有 count和 select信息
        for sku in skus:
            sku.count = cart[str(sku.id)]['count']
            sku.selected = cart[str(sku.id)]['selected']
        # 4.3 需要将模型转换为JSON
        serializer = CartSKUSerializer(skus, many=True)

        return Response(serializer.data)

    def put(self,request):
        """
        # 1.更新数据的提交和新增数据的提交是一样的，获取用户提交的数据，并且进行校验
        # 2.获取校验之后的数据
        # 3.获取用户信息，根据用户信息进行判断
        # 4.登陆用户更新redis
        # 5.未登陆用户更新cookies
        """
        # 1.更新数据的提交和新增数据的提交是一样的，获取用户提交的数据，并且进行校验
        serializer = CartSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        # 2.获取校验之后的数据
        sku_id = serializer.data.get('sku_id')
        count = serializer.data.get('count')
        selected = serializer.data.get('selected')
        # 3.获取用户信息，根据用户信息进行判断
        try:
            user = request.user
        except Exception:
            user = None
        if user is not None and user.is_authenticated:

            # 4.登陆用户更新redis
            #4.1连接reids
            redis_conn = get_redis_connection('cart')
            #4.2更新数据，幂操作，幂等操作是将最终的个数返回回来
            redis_conn.hset('cart_%s'%user.id,sku_id.count)
            # 是否选择要注意了
            if selected:
                #如果选中则添加到选中记录中
                redis_conn.sadd('cart_selected_%s'%user.id,sku_id)
            else:
                #如果没有选中，应该把选中列表中可能存在的sku_id删除　
                redis_conn.srem('cart_selected_%s'%user.id,sku_id)
            #4.3返回响应
            #注意，因为是采用的幂等，我们需要将服务器的最终结果返回回去
            return Response(serializer.data)
        else:

            # 5.未登陆用户更新cookies
            #5.1获取cookie数据，并且进行判断
            cookie_cart = request.COOKIES.get('cart')
            if cookie_cart is not None:
                cart = pickle.loads(base64.b64decode(cookie_cart))
            else:
                cart = {}

            #5.2更新数据
            #因为我们采用的幂等，最终的数据，所以我们只需要判断cart是否有对应的商品id
            # if sku_id in cart:
            cart[sku_id] = {
                'count':count,
                'selected':selected
            }
            #5.2返回
            response = Response(serializer.data)

            # 处理数据
            cookie_str = base64.b64encode(pickle.dumps(cart)).decode()
            response = Response(serializer.data)
            response.set_cookie('cart',cookie_str,24*3600)

            return response

    def delete(self,request):
        """
        # 删除数据
        # 思路
        # 获取提交数据，并进行校验
        # 获取校验之后的数据
        # 获取user信息
        # 判断是否登陆
        #     登陆用户，从redis中删除数据
        #     非登陆用户，从cookie中删除数据

        """

        # 获取提交数据，并进行校验
        serializer = CartDeleteSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        # 获取校验之后的数据
        sku_id = serializer.data.get('sku_id')
        # 获取user信息
        try:
            user = request.user
        except Exception:
            user = None

        # 判断是否登陆
        if user is not None and user.is_authenticated:

        #     登陆用户，从redis中删除数据
            #连接redis
            redis_conn = get_redis_connection('cart')
            # 删除哈希和集合中的数据
            redis_conn.hdel('cart_%s'%user.id,sku_id)
            redis_conn.srem('cart_selected_%s'%user.id,sku_id)
            #返回响应
            return Response(status=status.HTTP_204_NO_CONTENT)

        else:
        #     非登陆用户，从cookie中删除数据
            #获取cookie数据，并且判断是否存在
            cookie_cart = request.COOKIES.get('cart')
            if cookie_cart is not None:
                cart = pickle.loads(base64.b64decode(cookie_cart))
            else:
                cart = {}

            #删除
            if sku_id in cart:
                del cart[sku_id]

            #返回响应
            response = Response(status=status.HTTP_204_NO_CONTENT)

            #生成cookie字符串
            cookie_str = base64.b64encode(pickle.dumps(cart)).decode()
            response.set_cookie('cart',cookie_str,24*3600)

            return response





































